package com.chen.cloudpicture.api.aliyunai;

import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.chen.cloudpicture.api.aliyunai.model.CreateOutPaintingTaskRequest;
import com.chen.cloudpicture.api.aliyunai.model.CreateOutPaintingTaskResponse;
import com.chen.cloudpicture.api.aliyunai.model.GetOutPaintingTaskResponse;
import com.chen.cloudpicture.exception.BusinessException;
import com.chen.cloudpicture.exception.ErrorCode;
import com.chen.cloudpicture.exception.ThrowUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 阿里云百炼的绘画大模型 API 调用接口（图像画面扩展模型） 180天内前500次免费使用
 * 调用 AI 接口计算量大，较为耗费时间，需要异步调用，避免阻塞主线程。
 * 流程：客户端发送请求后，轮询服务端查询任务状态。（前端和后端都算客户端因为 AI服务器才算服务端）
 * 轮询也分前端轮询和后端轮询。前端轮询：前端间隔时长请求后端，后端请求AI服务端；后端轮询：前端一次发出请求后一直阻塞，后端间隔发出请求轮询状态。
 * 通常推荐前端轮询，因为后端轮询可能容易导致任务一直阻塞而耗尽资源。
 */
@Slf4j
@Component //要读取配置文件的值，所以要加这个注解
public class AliYunAiApi {

    @Value("${aliYunAi.apiKey}") // 读取配置文件
    private String apiKey;

    // 这些地址都在文档里找
    // 创建任务地址
    public static final String CREATE_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-painting";

    // 查询任务状态
    // 地址最后的 %s 是占位符，例如这里实际是替换{task_id}
    public static final String GET_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/tasks/%s";

    // 创建任务 (就是构建参数，发送网络请求，检查异常，然后返回响应结果)
    public CreateOutPaintingTaskResponse createOutPaintingTask(CreateOutPaintingTaskRequest request) {
        ThrowUtils.throwIf(request == null, ErrorCode.PARAMS_ERROR, "扩图参数为空");
        // 发送创建任务请求
        // 建议参照文档示例让 ai 写
        HttpRequest httpRequest = HttpRequest.post(CREATE_OUT_PAINTING_TASK_URL)
                .header(Header.AUTHORIZATION, "Bearer " + apiKey)
                .header("X-DashScope-Async", "enable") // 该模型要求必须开启异步处理，设置为enable
                .header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
                .body(JSONUtil.toJsonStr(request));
        // 处理响应
        try (HttpResponse response = httpRequest.execute()) { // try-with-resources, 自动关闭资源
            if (!response.isOk()) {
                log.error("请求异常: {}", response.body());
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败");
            }
            CreateOutPaintingTaskResponse createOutPaintingTaskResponse = JSONUtil.toBean(response.body(), CreateOutPaintingTaskResponse.class);
            if (createOutPaintingTaskResponse.getCode() != null) {
                log.error("AI 扩图失败: {}", createOutPaintingTaskResponse.getMessage());
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败");
            }
            return createOutPaintingTaskResponse;
        }
    }

    // 查询创建的任务结果
    public GetOutPaintingTaskResponse getOutPaintingTask(String taskId){
        ThrowUtils.throwIf(taskId == null, ErrorCode.PARAMS_ERROR, "任务 id 为空");
        // 发送查询任务请求
        String url = String.format(GET_OUT_PAINTING_TASK_URL, taskId);
        HttpRequest request = HttpRequest.get(url).header(Header.AUTHORIZATION, "Bearer " + apiKey);
        // 处理响应
        try(HttpResponse response = request.execute()) { // 其实前面 request 只是构建请求参数，没发出请求，.execute()得到 response 才是发送了请求
            ThrowUtils.throwIf(!response.isOk(), ErrorCode.OPERATION_ERROR, "查询任务失败");
            return JSONUtil.toBean(response.body(), GetOutPaintingTaskResponse.class);
        }
    }

}
